package com.quiz.quazz.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.json.JSONException;
import org.json.JSONObject;

import android.util.JsonReader;

import com.quiz.quazz.client.Player;
import com.quiz.quazz.client.PlayerList;
import com.quiz.quazz.util.Command;
import com.quiz.quazz.util.JSONHelper;

public class Server {
	private PlayerList playerList;

	private Map<String, Socket> clientsMap;

	private ServerSocket serverSocket;

	private OnMessageReceived messageListener;

	public Server() {
		clientsMap = new HashMap<String, Socket>();
		setPlayerList(new PlayerList());
	}

	public void startServer(Integer port) {
		final int serverPort = 50000 + port;
		final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(10);

		Runnable serverTask = new Runnable() {

			@Override
			public void run() {
				try {
					serverSocket = new ServerSocket(serverPort);
					System.out.println("Waiting for clients to connect...");
					while (true) {
						Socket clientSocket = serverSocket.accept();
						// if (!clientsMap.keySet().contains(clientSocket.getInetAddress())) {
						clientsMap.put(clientSocket.getInetAddress().getHostAddress(), clientSocket);
						clientProcessingPool.submit(new ClientTask(clientSocket));
						// }
					}
				} catch (IOException e) {
					System.err.println("Unable to process client request");
					e.printStackTrace();
				}
			}
		};
		Thread serverThread = new Thread(serverTask);
		serverThread.start();

	}

	public void stopServer() {
		try {
			for (Socket client : clientsMap.values()) {
				client.close();
			}
			serverSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void sendMessageToAllClients(String message) {
		System.out.println(clientsMap.size());
		for (Socket client : clientsMap.values()) {
			try {
				PrintWriter bufferSender =
					new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
				if (bufferSender != null && !bufferSender.checkError()) {
					bufferSender.println(System.currentTimeMillis() + " " + message);
					bufferSender.flush();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	private class ClientTask implements Runnable {
		private final Socket clientSocket;

		private ClientTask(Socket clientSocket) {
			this.clientSocket = clientSocket;
		}

		@Override
		public void run() {
			BufferedReader in;
			try {
				in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

				while (true) {

					String message = null;
					try {
						message = in.readLine();
					} catch (IOException e) {
						System.out.println("Error reading message: " + e.getMessage());
					}

					if (JSONHelper.isJSONValid(message)) {
						interpretMessage(message);
					}

					if (message != null && messageListener != null) {
						messageListener.messageReceived(message);
					}
				}
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}

	}

	private void interpretMessage(String message) {
		try {
			JSONObject json = new JSONObject(message);
			String type = json.getString("type");
			switch (Command.valueOf(type)) {
				case ADD_PLAYER: {
					String playerName = json.getString("name");
					String playerIp = json.getString("ip");
					Integer points = json.getInt("points");

					Player player = new Player(playerIp, playerName);
					player.setPoints(points);

					playerList.addPlayer(player);
				}
					break;
				case REMOVE_PLAYER: {
					String playerIp = json.getString("ip");
					playerList.removePlayerByIp(playerIp);
				}
					break;

				default:
					break;
			}
			sendPlayerListToAllClients();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void sendPlayerListToAllClients() {
		// TODO Auto-generated method stub

	}

	public void setMessageListener(OnMessageReceived messageListener) {
		this.messageListener = messageListener;
	}

	public PlayerList getPlayerList() {
		return playerList;
	}

	public void setPlayerList(PlayerList playerList) {
		this.playerList = playerList;
	}

	public interface OnMessageReceived {
		public void messageReceived(String message);
	}

}